#include "stdafx.h"

#include "FibEvent/IocpServerSocket.h"

#include "HttpFiberClient.h"

#include "FibEvent/BaseFiberClientHandler.h"
#include "http.h"

#include "wxtl/File.h"
#include "HttpServer.h"

#include <memory>

HttpFiberClient::HttpFiberClient(HttpServer* server)
	:m_server(server)
{
	
}

HttpFiberClient::~HttpFiberClient()
{

}

std::string ProcessUri(const char* root, const char* uri)
{
	std::string out;
	int uriLenght = strlen(uri);
	
	out.reserve(uriLenght + strlen(root));
	out.append(root);	

	for(int i = 0; i < uriLenght; i++)
	{
		if(uri[i] == '/')
		{
			out.append(1,'\\');
		}
		else if(uri[i] == '?')
		{
			break;
		}
		else
		{
			out.append(1, uri[i]);
		}
	}

	if(out.find("..") != std::string::npos)
	{
		return std::string();
	}

	return out;
}

void HttpFiberClient::Serve404(IocpIoBaseInitiator* io)
{
	HTTPDEBUG("{" << Get() << "} ERROR 404");
	HttpResponse resp;
	resp.status = "404 Not found";
	resp.dict.push_back(std::make_pair("Connection", "Close"));
	auto out = std::auto_ptr<char>(FormResponse(resp));
	io->Write(out.get(), strlen(out.get()));

}

bool HttpFiberClient::ServeProxy( IocpIoBaseInitiator* io, HttpLocationConfig* location,HttpRequest* request)
{
	HTTPDEBUG("{" << Get() << "}Serving proxy " << location->host);
	//this function is not optimal, because it does not support keep-alive and connection pooling
	char buffer[8193] = {0};

	if(location->host)
	{
		request->host = location->host;
	}
	
	std::auto_ptr<IocpServerSocket> proxySocket(m_server->CreateServerSocket());

	proxySocket->Connect(location->m_root, 80);
	
	auto out = std::auto_ptr<char>(FormRequest(*request));
	size_t bytes = proxySocket->Write(out.get(), strlen(out.get()));
	if(!bytes)
	{
		return false;
	}

	bytes = proxySocket->Read(buffer, WXARRAYSIZE(buffer) - 1);
	if(!bytes)
	{
		return false;
	}
	
	HttpResponse resp;
	if(!parseHttpResponse(buffer, strlen(buffer), resp))
	{
		return false;
	}

	out = std::auto_ptr<char>(FormResponse(resp));
	io->Write(out.get(), strlen(out.get()));

	//write current received chunk 
	io->Write(buffer + resp.headerLength, bytes - resp.headerLength);

	proxySocket->IocpCopy(io, resp.contentLenght - (bytes - resp.headerLength));

	return true;
}

void HttpFiberClient::DoWork( IocpIoBaseInitiator* io )
{
	char buffer[8193] = {0};
	try
	{
		while(1)
		{
			
			memset(buffer, 0, WXARRAYSIZE(buffer));
			size_t bytes = io->Read(buffer, WXARRAYSIZE(buffer) - 1);
			if(bytes == 0)
			{
				HTTPDEBUG("{" << Get() << "}End loop");
				return;
			}

			HttpRequest req;
			parseHttpRequest(buffer, strlen(buffer), req);

			HttpLocationConfig* location = ChooseLocation(req.uri.c_str());
			
			if(!location)
			{
				Serve404(io);
				break;
			}

			if(location->isProxy)
			{				
				if(!ServeProxy(io, location, &req))
				{

					Serve404(io);
					break;
				}
				continue;
			}

			std::string uri = ProcessUri(location->m_root, req.uri.c_str());
			
			if(!ServeFile(uri, io))
			{
				Serve404(io);
				break;
			}
		}
	}
	catch(...)
	{

	}

}

HttpLocationConfig* HttpFiberClient::ChooseLocation(const char* uri )
{
	HTTPDEBUG("{" << Get() << "}Choosing location for uri " << uri);
	
	
	string uriStrring = uri;
	
	for(size_t i = 0; i < m_server->m_config.m_locations; i++)
	{
		HttpLocationConfig* cLocation = &m_server->m_config.m_location[i];
		if(uriStrring.find(cLocation->m_url) == 0)
		{
			return cLocation;
		}
	}

	return NULL;
}

bool HttpFiberClient::ServeFile( std::string &uri, IocpIoBaseInitiator* io )
{
	HTTPDEBUG("{" << Get() << "}Serving file " << uri.c_str());
	wxtl::File file;
	HttpResponse resp;

	size_t fileLength = 0;

	if(!uri.empty() && 
		file.open(uri.c_str(), wxtl::File::accessRead| wxtl::File::shareReadWrite| wxtl::File::optionSequential))
	{
		resp.status = "200 OK";
		char respBuffer[64] = {0};
		size_t fileLength = (size_t)file.size();
		sprintf(respBuffer, "%i", fileLength);
		resp.dict.push_back(std::make_pair("Content-Length", respBuffer));
		resp.dict.push_back(std::make_pair("Connection", "Keep-Alive"));
	}
	else
	{
		return false;
	}

	auto out = std::auto_ptr<char>(FormResponse(resp));

	Buffer head;
	head.pointer = out.get();
	head.size = strlen(out.get());
	io->TransmitFile(&file, fileLength, &head, NULL);

	return true;
}